/* Copyright (c) 2008 Google Inc.
*  Copyright (c) 2009 Pierre Henri Kuate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace org.opensocial.client
{
    public enum Operation
    {
        contains,
        equals,
        startsWith,
        present,
    }
    public enum Format
    {
        atom,
        json,
        xml,
    }
    public enum SortOrder
    {
        ascending,
        descending,
    }


    public interface IOpenSocialRequest
    {
        string Id { get; set; }
        string RestPathComponent { get; }
        string RpcMethod { get; }
        string RestMethod { get; }

        System.Collections.Generic.IDictionary<string, OpenSocialRequestParameter> Parameters { get; set; }

        bool hasParameter(string key);
        string getParameter(string parameter);
        string popParameter(string key);

        string ToJson();
        Jayrock.Json.JsonObject getParameterJsonObject();
        string getParameterJson();
    }


    /// <summary> An object which represents a single OpenSocial REST/JSON-RPC request, which
    /// are collected and submitted using one or more OpenSocialBatch instances.
    /// Instances should not be created directly but should instead be returned
    /// from static OpenSocialClient methods which take care to set all
    /// properties appropriately given the request type.
    /// 
    /// </summary>
    /// <author>  Jason Cooper
    /// </author>
    public class OpenSocialRequest<T> : IOpenSocialRequest
        where T : data.OpenSocialObject
    {
        /// <summary> Returns instance variable: restPathComponent. If path component does not
        /// have a trailing backslash, one is added before being returned.
        /// </summary>
        public string RestPathComponent { get; private set; }
        public string RpcMethod { get; private set; }
        public string RestMethod { get; private set; }

        public OpenSocialRequest(string restPathComponent, string restMethod, string rpcMethod)
        {
            Parameters = new System.Collections.Generic.Dictionary<string, OpenSocialRequestParameter>();

            RestPathComponent = restPathComponent;
            RestMethod = restMethod;
            RpcMethod = rpcMethod;
        }

        public OpenSocialRequest(string restPathComponent, string rpcMethod)
            : this(restPathComponent, "GET", rpcMethod)
        {
        }

        /// <summary> Gets or sets instance variable: id.</summary>
        public string Id
        {
            get; set;
        }

        public System.Collections.Generic.IDictionary<string, OpenSocialRequestParameter> Parameters
        {
            get; set;
        }


        /// <summary> Creates a new entry in parameters Map with the passed key and value;
        /// used for setting request-specific parameters such as appId, userId,
        /// and groupId.
        /// </summary>
        public OpenSocialRequest<T> addParameter(string key, params string[] values)
        {
            Parameters.Add(key, new OpenSocialRequestParameter(values));
            return this;
        }

        /// <summary>
        /// Returns true if a parameter with the given key is registered, false otherwise.
        /// </summary>
        public bool hasParameter(string key)
        {
            return Parameters.ContainsKey(key);
        }

        /// <summary> Returns the value of the parameter with the given name or null if
        /// no parameter with that name exists.
        /// </summary>
        public string getParameter(string parameter)
        {
            OpenSocialRequestParameter value;
            return Parameters.TryGetValue(parameter, out value) ? value.getValuesString() : null;
        }

        public string popParameter(string key)
        {
            string value = getParameter(key);
            Parameters.Remove(key);

            return value;
        }


        #region Set methods for Standard Query Parameters

        public OpenSocialRequest<T> setParameter(string key, params string[] values)
        {
            Parameters[key] = new OpenSocialRequestParameter(values);
            return this;
        }
        public OpenSocialRequest<T> setCount(int count)
        {
            Parameters["count"] = new OpenSocialRequestParameter(count.ToString());
            return this;
        }
        public OpenSocialRequest<T> setFilterBy(string fieldname)
        {
            Parameters["filterBy"] = new OpenSocialRequestParameter(fieldname);
            return this;
        }
        public OpenSocialRequest<T> setFilterOp(Operation operation)
        {
            Parameters["filterOp"] = new OpenSocialRequestParameter(operation.ToString());
            return this;
        }
        public OpenSocialRequest<T> setFilterValue(string value) // Note: Accept @owner and @viewer
        {
            Parameters["filterValue"] = new OpenSocialRequestParameter(value);
            return this;
        }
        public OpenSocialRequest<T> setFormat(Format format)
        {
            Parameters["format"] = new OpenSocialRequestParameter(format.ToString());
            return this;
        }
        public OpenSocialRequest<T> setFields(params string[] values) // Note: @all is accepted to indicate returning all available fields.
        {
            Parameters["fields"] = new OpenSocialRequestParameter(values);
            return this;
        }
        public OpenSocialRequest<T> setNetworkDistance(string networkDistance)
        {
            Parameters["networkDistance"] = new OpenSocialRequestParameter(networkDistance);
            return this;
        }
        public OpenSocialRequest<T> setSortBy(string fieldname)
        {
            Parameters["sortBy"] = new OpenSocialRequestParameter(fieldname);
            return this;
        }
        public OpenSocialRequest<T> setSortOrder(SortOrder order)
        {
            Parameters["sortOrder"] = new OpenSocialRequestParameter(order.ToString());
            return this;
        }
        public OpenSocialRequest<T> setStartIndex(int startIndex)
        {
            Parameters["startIndex"] = new OpenSocialRequestParameter(startIndex.ToString());
            return this;
        }
        public OpenSocialRequest<T> setUpdatedSince(System.DateTime xsdDateTime)
        {
            Parameters["updatedSince"] = new OpenSocialRequestParameter(xsdDateTime.ToString("u"));
            return this;
        }

        #endregion


        public data.Response<T> requestCollection(OpenSocialClient client)
        {
            var batch = new OpenSocialBatch();
            batch.addRequest(this, "request");

            return batch.send(client).getItemAsCollection<T>("request", client.UseRpc, null);
        }


        public T requestObject(OpenSocialClient client)
        {
            var batch = new OpenSocialBatch();
            batch.addRequest(this, "request");

            return batch.send(client).getItemAsObject<T>("request", null);
        }




        /// <summary> Returns a JSON-RPC serialization of the request including ID, RPC method,
        /// and all added parameters. Used by other classes when preparing to submit
        /// an RPC batch request.
        /// </summary>
        /// <throws>  JSONException </throws>
        public string ToJson()
        {
            var o = new Jayrock.Json.JsonObject();

            if (Id != null)
            {
                o.Put("id", Id);
            }

            o.Put("method", RpcMethod);
            o.Put("params", getParameterJsonObject());

            return o.ToString();
        }

        public Jayrock.Json.JsonObject getParameterJsonObject()
        {
            var valuesMap = new System.Collections.Generic.Dictionary<string, object>();

            foreach (var entry in Parameters)
            {
                if (entry.Value.isMultivalued())
                {
                    var strings = entry.Value.getValues();
                    valuesMap[entry.Key] = new Jayrock.Json.JsonArray(strings);
                }
                else
                {
                    valuesMap[entry.Key] = entry.Value.getValuesString();
                }
            }

            return new Jayrock.Json.JsonObject(valuesMap);
        }

        public string getParameterJson()
        {
            return getParameterJsonObject().ToString();
        }
    }
}